<?php

require_once APP_DIR . DS . 'models' . DS . 'logic' . DS . 'workflow' . DS . 'state';
require_once APP_DIR . DS . 'models' . DS . 'logic' . DS . 'workflow' . DS . 'event';
require_once APP_DIR . DS . 'models' . DS . 'logic' . DS . 'workflow' . DS . 'action';
require_once APP_DIR . DS . 'models' . DS . 'logic' . DS . 'workflow' . DS . 'transition';

class Dia extends Diagram {
    const STATE = "UML - State";
    const TRANSITION = "UML - Transition";
    const FROM_STATE = "0";
    const TO_STATE = "1";
    const EVENT = "trigger";
    const ACTION = "action";
    const GUARD = "guard";
    const ENTRY_ACTION = "entry_action";
    const DO_ACTION = "do_action";
    const EXIT_ACTION = "exit_action";
    const MIME_TYPE = 4;
    const XML = "XML  document text";
    const GZIP = "gzip compressed data";
    const CONTENT = "content";
    const ENVELOPE = "envelope";

    private function removeNumerals($string) {
        return substr($string, 1,strlen($string) - 2);;
    }

    private function parseStates($xml) {
        $states = $xml->xpath("/dia:diagram/dia:layer/dia:object[@type='" . Dia::STATE . "']");

        foreach ($states as $currentState) {
            $id = $currentState->attributes()->id;
            $text = $xml->xpath("/dia:diagram/dia:layer/dia:object[@id='$id']/dia:attribute[@name='text']/dia:composite[@type='text']/dia:attribute[@name='string']/dia:string");
            $name = $this->removeNumerals($text[0]);

            $entryData = $xml->xpath("/dia:diagram/dia:layer/dia:object[@id='$id']/dia:attribute[@name='". Dia::ENTRY_ACTION ."']/dia:string");
            $entry = $this->removeNumerals($entryData[0]);

            $doData = $xml->xpath("/dia:diagram/dia:layer/dia:object[@id='$id']/dia:attribute[@name='". Dia::DO_ACTION ."']/dia:string");
            $do = $this->removeNumerals($doData[0]);

            $exitData = $xml->xpath("/dia:diagram/dia:layer/dia:object[@id='$id']/dia:attribute[@name='". Dia::EXIT_ACTION ."']/dia:string");
            $exit = $this->removeNumerals($exitData[0]);

            $state = new State($name, $id);
            $state->setAction(new Action($entry), State::ON_ENTRY);
            $state->setAction(new Action($do), State::ON_DO);
            $state->setAction(new Action($exit), State::ON_EXIT);
            $this->addState($state);
        }
    }

    private function parseTransitions($xml) {
        $transitions = $xml->xpath("/dia:diagram/dia:layer/dia:object[@type='" . Dia::TRANSITION . "']");

        foreach ($transitions as $currentTransition) {
            $id = $currentTransition->attributes()->id;
            $stringEvent = $xml->xpath("/dia:diagram/dia:layer/dia:object[@id='$id']/dia:attribute[@name='". Dia::EVENT ."']/dia:string");
            // elimino los numerales
            // i remove the numerals
            $nameEvent = $this->removeNumerals($stringEvent[0]);

            $stringAction = $xml->xpath("/dia:diagram/dia:layer/dia:object[@id='$id']/dia:attribute[@name='". Dia::ACTION ."']/dia:string");
            $nameAction = $this->removeNumerals($stringAction[0]);

            $stringGuard = $xml->xpath("/dia:diagram/dia:layer/dia:object[@id='$id']/dia:attribute[@name='". Dia::GUARD ."']/dia:string");
            $guard = $this->removeNumerals($stringGuard[0]);

            $dataFrom = $xml->xpath("/dia:diagram/dia:layer/dia:object[@id='$id']/dia:connections/dia:connection[@handle='" . Dia::FROM_STATE . "']");
            $stateFromId = $dataFrom[0]->attributes()->to;

            $dataTo = $xml->xpath("/dia:diagram/dia:layer/dia:object[@id='$id']/dia:connections/dia:connection[@handle='" . Dia::TO_STATE . "']");
            $stateToId = $dataTo[0]->attributes()->to;

            $states = $this->getStates();
            $stateFrom = null;
            $stateTo = null;
            foreach ($states as $state) {
                // si los estados ya fueron encontrados
                // entonces me termino el bucle
                // if we have found the states, we break the loop then
                if ($stateFrom && $stateTo) break;
                
                if (!$stateFrom && $state->getId() == $stateFromId){
                    $stateFrom = $state;
                }
                if (!$stateTo && $state->getId() == $stateToId){
                    $stateTo = $state;
                }
            }

            $event = new Event($nameEvent);
            $action = new Action($nameAction);
            $transition = new Transition($stateFrom, $stateTo, $event, $action, $guard);

            $this->addAction($action);
            $this->addEvent($event);
            $this->addGuard($guard);
            $this->addTransition($transition);
        }
    }

    private function getMimeType($file) {
        $finfo = finfo_open(Dia::MIME_TYPE);
        $mimeString = finfo_file($finfo, $file);
        finfo_close($finfo);

        $mime = array();
        $dataRaw = explode("(", $mimeString);
        $mime[Dia::CONTENT] = trim($dataRaw[0]);
        if (isset ($dataRaw[1])) {
            $extra = explode(",", $dataRaw[1]);
            $mime[Dia::ENVELOPE] = trim($extra[0]);
        }

        return $mime;
    }

    private function getXML($file) {
        if (!file_exists($file)) throw new Exception('The file does not exists');
        $isUrl = true;
        $mime = $this->getMimeType($file);
        // si el archivo contiene un xml
        // is the file a xml
        if ($mime[Dia::CONTENT] == Dia::XML) {
            // esta comprimido...
            // is it comppressed...
            if (isset ($mime[Dia::ENVELOPE])) {
                // con Gzip?
                // with Gzip?
                if ($mime[Dia::ENVELOPE] == Dia::GZIP) {
                    // obtengo la cadena
                    // I get the string
                    $xml = $this->decompress($file);
                    // indico que es cadena
                    // it is a string
                    $isUrl = false;
                }
                else {
                    throw new Exception("It's not a valid Dia file format");
                }
            }
            // si no estaba comprimido
            // if the file was not compressed
            if ($isUrl) {
                $xml = $file;
            }
        }
        else {
            throw new Exception("It's not a valid Dia file format");
        }
        return new SimpleXMLElement($xml,NULL,$isUrl,"dia",TRUE);


    }

    private function decompress($file) {
        $data = gzfile($file);
        $data = implode($data);
        return $data;
    }

    protected function parse($file){
        // obtengo el objeto xml
        // I get the xml object
        $xml = $this->getXML($file);

        // leo y asigno los estados
        // I read and set the states
        $this->parseStates($xml);

        // leo y asigno las transiciones
        // I read and set the transitions
        $this->parseTransitions($xml);
    }
}

?>
